using System;
using System.Runtime.InteropServices;

namespace Clandestine.Input.OSX
{
	internal static class HIDUtilities
	{	
		/* Usage Pages -- We only need GenericDesktop & Button (more in IOHIDUsageTables.h) */
		public enum UsagePages 
		{
			kHIDPage_Undefined	= 0x00,
			kHIDPage_GenericDesktop	= 0x01,
			kHIDPage_Button	= 0x09
		}
		
		/* GenericDesktop Page (0x01) */
		public enum GenericDesktop
		{
			kHIDUsage_GD_Pointer	= 0x01,	/* Physical Collection */
			kHIDUsage_GD_Mouse	= 0x02,	/* Application Collection */
			/* 0x03 Reserved */
			kHIDUsage_GD_Joystick	= 0x04,	/* Application Collection */
			kHIDUsage_GD_GamePad	= 0x05,	/* Application Collection */
			kHIDUsage_GD_Keyboard	= 0x06,	/* Application Collection */
			kHIDUsage_GD_Keypad	= 0x07,	/* Application Collection */
			kHIDUsage_GD_MultiAxisController	= 0x08,	/* Application Collection */
			/* 0x09 - 0x2F Reserved */
			kHIDUsage_GD_X	= 0x30,	/* Dynamic Value */
			kHIDUsage_GD_Y	= 0x31,	/* Dynamic Value */
			kHIDUsage_GD_Z	= 0x32,	/* Dynamic Value */
			kHIDUsage_GD_Rx	= 0x33,	/* Dynamic Value */
			kHIDUsage_GD_Ry	= 0x34,	/* Dynamic Value */
			kHIDUsage_GD_Rz	= 0x35,	/* Dynamic Value */
			kHIDUsage_GD_Slider	= 0x36,	/* Dynamic Value */
			kHIDUsage_GD_Dial	= 0x37,	/* Dynamic Value */
			kHIDUsage_GD_Wheel	= 0x38,	/* Dynamic Value */
			kHIDUsage_GD_Hatswitch	= 0x39,	/* Dynamic Value */
			kHIDUsage_GD_CountedBuffer	= 0x3A,	/* Logical Collection */
			kHIDUsage_GD_ByteCount	= 0x3B,	/* Dynamic Value */
			kHIDUsage_GD_MotionWakeup	= 0x3C,	/* One-Shot Control */
			kHIDUsage_GD_Start	= 0x3D,	/* On/Off Control */
			kHIDUsage_GD_Select	= 0x3E,	/* On/Off Control */
			/* 0x3F Reserved */
			kHIDUsage_GD_Vx	= 0x40,	/* Dynamic Value */
			kHIDUsage_GD_Vy	= 0x41,	/* Dynamic Value */
			kHIDUsage_GD_Vz	= 0x42,	/* Dynamic Value */
			kHIDUsage_GD_Vbrx	= 0x43,	/* Dynamic Value */
			kHIDUsage_GD_Vbry	= 0x44,	/* Dynamic Value */
			kHIDUsage_GD_Vbrz	= 0x45,	/* Dynamic Value */
			kHIDUsage_GD_Vno	= 0x46,	/* Dynamic Value */
			/* 0x47 - 0x7F Reserved */
			kHIDUsage_GD_SystemControl	= 0x80,	/* Application Collection */
			kHIDUsage_GD_SystemPowerDown	= 0x81,	/* One-Shot Control */
			kHIDUsage_GD_SystemSleep	= 0x82,	/* One-Shot Control */
			kHIDUsage_GD_SystemWakeUp	= 0x83,	/* One-Shot Control */
			kHIDUsage_GD_SystemContextMenu	= 0x84,	/* One-Shot Control */
			kHIDUsage_GD_SystemMainMenu	= 0x85,	/* One-Shot Control */
			kHIDUsage_GD_SystemAppMenu	= 0x86,	/* One-Shot Control */
			kHIDUsage_GD_SystemMenuHelp	= 0x87,	/* One-Shot Control */
			kHIDUsage_GD_SystemMenuExit	= 0x88,	/* One-Shot Control */
			kHIDUsage_GD_SystemMenu	= 0x89,	/* Selector */
			kHIDUsage_GD_SystemMenuRight	= 0x8A,	/* Re-Trigger Control */
			kHIDUsage_GD_SystemMenuLeft	= 0x8B,	/* Re-Trigger Control */
			kHIDUsage_GD_SystemMenuUp	= 0x8C,	/* Re-Trigger Control */
			kHIDUsage_GD_SystemMenuDown	= 0x8D,	/* Re-Trigger Control */
			/* 0x8E - 0x8F Reserved */
			kHIDUsage_GD_DPadUp	= 0x90,	/* On/Off Control */
			kHIDUsage_GD_DPadDown	= 0x91,	/* On/Off Control */
			kHIDUsage_GD_DPadRight	= 0x92,	/* On/Off Control */
			kHIDUsage_GD_DPadLeft	= 0x93,	/* On/Off Control */
			/* 0x94 - 0xFFFF Reserved */
			kHIDUsage_GD_Reserved = 0xFFFF
		}
		
		public enum IOHIDElementType 
		{
			kIOHIDElementTypeInput_Misc        = 1,
			kIOHIDElementTypeInput_Button      = 2,
			kIOHIDElementTypeInput_Axis        = 3,
			kIOHIDElementTypeInput_ScanCodes   = 4,
			kIOHIDElementTypeOutput            = 129,
			kIOHIDElementTypeFeature           = 257,
			kIOHIDElementTypeCollection        = 513
		};
		
		[Flags]
		public enum HIDElementTypeMask 
		{
		    kHIDElementTypeInput = 1 << 1,
		    kHIDElementTypeOutput = 1 << 2,
		    kHIDElementTypeFeature = 1 << 3,
		    kHIDElementTypeCollection = 1 << 4,
		    kHIDElementTypeIO = kHIDElementTypeInput | kHIDElementTypeOutput | kHIDElementTypeFeature,
		    kHIDElementTypeAll = kHIDElementTypeIO | kHIDElementTypeCollection
		}
	
		public enum IOHIDValueScaleType
		{
    		kIOHIDValueScaleTypeCalibrated,
    		kIOHIDValueScaleTypePhysical	
    	}
	
		// builds list of device with elements (allocates memory and captures devices)
		// list is allcoated internally within HID Utilites and can be accessed via accessor functions
		// structures within list are considered flat and user accessable, butnot user modifiable
		// can be called again to rebuild list to account for new devices (will do the right thing in case of disposing existing list)
		[DllImport("libHID_Utilities.dylib")]
		public static extern char HIDBuildDeviceList (uint usagePage, uint usage);

		// release list built by above function
		// MUST be called prior to application exit to properly release devices
		// if not called (or app crashes) devices can be recovered by pluging into different location in USB chain
		[DllImport("libHID_Utilities.dylib")]
		public static extern void HIDReleaseDeviceList ();
		
		// does a device list exist
		[DllImport("libHID_Utilities.dylib")]
		public static extern bool HIDHaveDeviceList ();
		
		// how many HID devices have been found
		// returns 0 if no device list exist
		[DllImport("libHID_Utilities.dylib")]
		public static extern uint HIDCountDevices ();
		
		// get the first device in the device list
		// returns NULL if no list exists or it's empty
		[DllImport("libHID_Utilities.dylib")]
		public static extern IntPtr HIDGetFirstDevice();
		
		// Purpose: get the usage for this device
    	// Returns: uint32_t - the usage for this device
    	[DllImport("libHID_Utilities.dylib")]
    	public static extern uint IOHIDDevice_GetPrimaryUsage(IntPtr inIOHIDDeviceRef);
    	
		// Purpose: get the usage page for this device
		// Returns: uint32_t - the usage page for this device
		[DllImport("libHID_Utilities.dylib")]
		public static extern uint IOHIDDevice_GetPrimaryUsagePage(IntPtr inIOHIDDeviceRef);
    	    
	    // Purpose: validate this device
	    // Inputs:  inIOHIDDeviceRef - the IDHIDDeviceRef for this device
	    // Returns: Boolean         - TRUE if we find the device in our( internal ) device list
	    [DllImport("libHID_Utilities.dylib")]
	    public static extern bool HIDIsValidDevice(IntPtr inIOHIDDeviceRef);
	    
	    // get next device in list given current device as parameter
		// returns NULL if end of list
 		[DllImport("libHID_Utilities.dylib")]
		public static extern IntPtr HIDGetNextDevice(IntPtr currentDevice);
		
		// how many elements does a specific device have
		// returns 0 if device is invlaid or NULL
		[DllImport("libHID_Utilities.dylib")]
		public static extern uint HIDCountDeviceElements(IntPtr inIOHIDDeviceRef, uint typeMask);
		
		// get the first element of device passed in as parameter
		// returns NULL if no list exists or device does not exists or is NULL
		[DllImport("libHID_Utilities.dylib")]
		public static extern IntPtr HIDGetFirstDeviceElement(IntPtr inIOHIDDeviceRef, uint typeMask);
		
		// get next element of given device in list given current element as parameter
		// will walk down each collection then to next element or collection (depthwise traverse)
		// returns NULL if end of list
		// uses mask of HIDElementTypeMask to restrict element found
		// use kHIDElementTypeIO to get previous HIDGetNextDeviceElement functionality
		[DllImport("libHID_Utilities.dylib")]
		public static extern IntPtr HIDGetNextDeviceElement(IntPtr inIOHIDElementRef, uint typeMask);
		
		// Purpose: returns the current value for an element( polling )
		// Notes:   will return 0 on error conditions which should be accounted for by application	
		// Inputs:  inElementRef    - the element
		//          inIOHIDValueScaleType   - scale type ( calibrated or physical )
		// Returns: double      - current value for element
		[DllImport("libHID_Utilities.dylib")]
		public static extern double IOHIDElement_GetValue(IntPtr inElementRef, uint inIOHIDValueScaleType);
		
		/* I don't quite believe HIDUtilities leaves us up the duff and requires we use these IOKit functions...
		   it doesn't matter, as we use them exactly the same as if they weren't but, hey. */
		
		// Get element type [From IOKit]
		[DllImport("IOKit")]
		public static extern uint IOHIDElementGetType(IntPtr element);  
		
		// Get element usage page [From IOKit]
		[DllImport("IOKit")]
		public static extern uint IOHIDElementGetUsagePage(IntPtr element);  
		
		// Get element usage [From IOKit]
		[DllImport("IOKit")]
		public static extern uint IOHIDElementGetUsage(IntPtr element);  
		
		// Get element max value [From IOKit]
		[DllImport("IOKit")]
		public static extern uint IOHIDElementGetPhysicalMax(IntPtr element);  
		
		// Get element min value [From IOKit]
		[DllImport("IOKit")]
		public static extern uint IOHIDElementGetPhysicalMin(IntPtr element); 
	}
}

